﻿<?php

//////////////////////////////////////////////////////////////////////////
//
// WikiHiero2 - A PHP convert from text using "Manual for the encoding of
// hieroglyphic texts for computer input" syntax to HTML entities (table and
// images).
//
// Copyright (C) 2004-2007 Guillaume Blanchard (Aoineko)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
//////////////////////////////////////////////////////////////////////////

require_once("wh_tools.php");
require_once("wh_class.php");
require_once("wh_node.php");

define("WH_DEBUG_MODE",         0); // 0 - 3

//define("WH_OPERATOR_SEPARATOR",     0); // -
//define("WH_OPERATOR_SUBORDINATION", 1); // :
//define("WH_OPERATOR_JUXTAPOSITION", 2); // *
//define("WH_OPERATOR_PARENTHESIS",   3); // ()

define("WH_OPERATOR_SUBORDINATION", "subdiv"); // :
define("WH_OPERATOR_JUXTAPOSITION", "juxtapos"); // *
define("WH_OPERATOR_LIGATURE",      "ligature"); // &

			
////////////////////////////////////////////////////////////////////////////////
/**
 * WikiHiero Serializer class
 */
class WH_Document
{
	protected $root;      // Root node.
	protected $header;    // Current header.
	protected $biblio;    // Current biblio.
	protected $area;      // Current area.
	protected $line;      // Current hieroglyphs line.
	protected $group;     // Current group.
	protected $cartouche; // Current cartouche.
	protected $syntax;    // 'Syntax' root node.
	protected $section;   // Current section.
	protected $setence;   // Current sentence.
	protected $word;      // Current word.

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Initialize the document.
	 * @param   $create  Create the root node [Boolean=true].
	 */
	public function __construct($create = true)
	{
		WH_Node::ResetIndex();
		if($create)
			$this->root = new WH_Node(null, WH_CLASS_DOC);
		else
			$this->root = null;

		$this->area = null;
		$this->line = null;
		$this->group = null;
		$this->cartouche = null;
		$this->syntax = null;
		$this->section = null;
		$this->setence = null;
		$this->word = null;
	}
	
	// getElementsByTagName
	// getElementById

	/**
	 * Get the root node.
	 * @return  A reference to the root node.
	 */
	public function &GetRoot() { return $this->root; }

	/**
	 * Create a new description in a node.
	 * @param   $parent  The parent where to add the area [WH_Node=$this->root].
	 * @param   $content	Node content.
	 * @param   $lang		Description langage [optional].
	 * @param   $source		Description source [optional].
	 * @param   $page		Source page [optional].
	 * @param   $paragraph	Source paragraph [optional].
	 */
	public function &AddDesc(&$parent, $content, $lang = null, $source = null, $page = null, $paragraph = null)
	{
		if($parent == null)
			$parent = &$this->root;
		if($parent == null)
			WH_Error("Try to create an desc in invalid node! (parent == null)");

		$desc = &$parent->AddChild(WH_CLASS_DESC);
		$desc->SetAttribute(WH_Attribute::Content, $content);
		if($lang !== null)
			$desc->SetAttribute(WH_Attribute::Langage, $lang);
		if($source !== null)
			$desc->SetAttribute(WH_Attribute::Source, $source);
		if($page !== null)
			$desc->SetAttribute(WH_Attribute::Page, $page);
		if($paragraph !== null)
			$desc->SetAttribute(WH_Attribute::Paragraph, $paragraph);
			
		return $desc;
	}

	/**
	 * Create a new area in the document.
	 * @return  TRUE if syntax node created ; FALSE if the node is already existing.
	 */
	public function EnableSyntax()
	{
      if($this->root == null)
			WH_Error("Try to create syntax hierachy in a non-initialized document!");

		foreach($this->root->GetChildren() as $child)
			if($child->GetClass() == WH_CLASS_SYNTAX)
				return false;

		$this->syntax = &$this->root->AddChild(WH_CLASS_SYNTAX);
		return true;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a header in the document.
	 * @param   $parent  The parent where to add the area [WH_Node=$this->root].
	 */
	public function &AddHeader(&$parent = null)
	{
		if($parent == null)
			WH_Error("Try to create an desc in invalid node! (parent == null)");
		if($parent->GetClass() != WH_CLASS_DOC)
			WH_Error("Try to create a header in invalid node type (must be WH_CLASS_DOC)!");
			
		$node = &$parent->AddChild(WH_CLASS_HEADER);
		return $node;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a author in the document.
	 * @param   $parent  	The parent where to add the author.
	 * @param   $content	Node content.
	 */
	public function &AddAuthor(&$parent = null, $content)
	{
		if($parent == null)
			WH_Error("Try to create an desc in invalid node! (parent == null)");
		if($parent->GetClass() != WH_CLASS_HEADER)
			WH_Error("Try to create a header in invalid node type (must be WH_CLASS_HEADER)!");
			
		$node = &$parent->AddChild(WH_CLASS_AUTHOR);
     	$node->SetAttribute(WH_Attribute::Content, $content);
		return $node;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a date in the document.
	 * @param   $parent  	The parent where to add the date.
	 * @param   $content	Node content.
	 */
	public function &AddDate(&$parent = null, $content)
	{
		if($parent == null)
			WH_Error("Try to create an desc in invalid node! (parent == null)");
		if($parent->GetClass() != WH_CLASS_HEADER)
			WH_Error("Try to create a header in invalid node type (must be WH_CLASS_HEADER)!");
			
		$node = &$parent->AddChild(WH_CLASS_DATE);
     	$node->SetAttribute(WH_Attribute::Content, $content);
		return $node;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a bilbio in the document.
	 * @param   $parent  The parent where to add the bibliography.
	 */
	public function &AddBiblio(&$parent = null)
	{
		if($parent == null)
			WH_Error("Try to create an desc in invalid node! (parent == null)");
		if($parent->GetClass() != WH_CLASS_HEADER)
			WH_Error("Try to create a header in invalid node type (must be WH_CLASS_HEADER)!");
			
		$node = &$parent->AddChild(WH_CLASS_BIBLIO);
		return $node;
	}
	
	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a reference in the document.
	 * @param   $parent  	The parent where to add the reference.
	 * @param   $lang       Reference langage [optional]
	 * @param   $name       Reference name [optional]
	 * @param   $title      Reference title
	 * @param   $author     Reference author [optional]
	 * @param   $publisher  Reference publisher [optional]
	 * @param   $address    Reference publishing address [optional]
	 * @param   $year       Reference publishing date [optional]
	 */
	public function &AddReference(&$parent = null, $lang = null, $name = null,
		$title, $author = null, $publisher = null, $address = null,
		$year = null)
	{
		if($parent == null)
			WH_Error("Try to create an desc in invalid node! (parent == null)");
		if($parent->GetClass() != WH_CLASS_BIBLIO)
			WH_Error("Try to create a header in invalid node type (must be WH_CLASS_BIBLIO)!");
			
		$node = &$parent->AddChild(WH_CLASS_REFERENCE);
		
		$node->SetAttribute(WH_Attribute::Title, $title);
		if($lang !== null)
			$node->SetAttribute(WH_Attribute::Langage, $lang);
		if($name !== null)
			$node->SetAttribute(WH_Attribute::Name, $name);
		if($author !== null)
      		$node->SetAttribute(WH_Attribute::Author, $author);
		if($publisher !== null)
			$node->SetAttribute(WH_Attribute::Publisher, $publisher);
		if($address !== null)
			$node->SetAttribute(WH_Attribute::Address, $address);
		if($year !== null)
			$node->SetAttribute(WH_Attribute::Year, $year);

		return $node;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a new area in the document.
	 * @param   $parent  The parent where to add the area [WH_Node=$this->root].
	 * @param   $type    The type of the area [string=null].
	 * @param   $name    The name of the area [string=null].
	 */
	public function &AddArea(&$parent = null, $type = null, $name = null, $pos = null, $size = null)
	{
		if($parent == null)
			$parent = &$this->root;
		if($parent == null)
			WH_Error("Try to create an area in a non-initialized document!");

		$level = 0;
		if($parent->IsAttributeSet(WH_Attribute::Level))
			$level = $parent->GetAttribute(WH_Attribute::Level);
		$level++;

		$this->area = &$parent->AddChild(WH_CLASS_AREA);
		$this->area->SetAttribute(WH_Attribute::Level, $level);
		if($type !== null)
			$this->area->SetAttribute(WH_Attribute::Type, $type);
		if($name !== null)
			$this->area->SetAttribute(WH_Attribute::Name, $name);
		if($pos !== null)
			$this->area->SetAttribute(WH_Attribute::Position, $pos);
		if($size !== null)
			$this->area->SetAttribute(WH_Attribute::Size, $size);

		return $this->area;
	}

	/**
	 * Get the current area.
	 * @return	A reference to the current area.
	 */
	public function &GetCurrentArea() { return $this->area; }

	/**
	 * Get the number of pages of the document.
	 * @param   $parent  The parent where to count the area [WH_Node=$this->root].
	 */
	public function GetAreaNumber(&$parent = null)
	{
		if($parent == null)
			$parent = &$this->root;
		if($parent == null)
			return 0;

		$count = 0;
		foreach($parent->GetChildren() as $child)
			if($child->GetClass() == WH_CLASS_AREA)
				$count++;

		return $count;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a new row in the current page.
	 * @param   $parent  The parent where to add the line [WH_Node=$this->root].
	 * @param   $dir		The line direction (ltr|rtl).
	 * @param   $oreint	The line orientation (horizontal|vertical).
	 * @return  Reference to the created node
	 */
	public function &AddLine(&$parent = null, $dir = null, $orient = null)
	{
		if($parent == null)
			$parent = &$this->area;
		if($parent == null)
			WH_Error("Try to create a line in a inexisting area!");
		if($parent->GetClass() != WH_CLASS_AREA)
			WH_Error("Try to create a line in a non-area node!");

		$this->line = &$parent->AddChild(WH_CLASS_LINE);
		if($dir !== null)
      	$this->line->SetAttribute(WH_Attribute::Direction, $dir);
		if($orient !== null)
      	$this->line->SetAttribute(WH_Attribute::Orientation, $orient);

		return $this->line;
	}

	/// Create a new page in the document.
	public function &GetCurrentLine() { return $this->line; }

	/**
	 * Get the number of line of the given area.
	 * @param   $parent  The parent where to count the area [WH_Node=$this->root].
	 */
	public function GetLineNumber(&$parent = null)
	{
		if($parent == null)
			$parent = &$this->area;
      if($parent == null)
			WH_Error("Try to create a line in a inexisting area!");

		if($parent->GetClass() != WH_CLASS_AREA)
			WH_Error("Try to create a line in a non-area node!");

		$count = 0;
		foreach($parent->GetChildren() as $child)
			if($child->GetClass() == WH_CLASS_LINE)
				$count++;

		return $count;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a new row in the current page.
	 * @param   $parent  The parent where to add the line [WH_Node].
	 * @param   $code  	Gardiner code of the hiero [string=null].
	 * @param   $parent  Phoneme value of the hiero [string=null].
	 * @return  Reference to the created node
	 */
	public function &AddHiero(&$parent, $code = null, $phoneme = null)
	{
		switch($parent->GetClass())
		{
         case WH_CLASS_LINE:
         case WH_CLASS_LAYOUT:
         case WH_CLASS_GROUP:
         case WH_CLASS_FRAME:
				break;
			default:
				WH_Error("Try to create a hieroglyph in a non-valid node!");
		}

		$hiero = &$parent->AddChild(WH_CLASS_HIEROGLYPH);
		if($code !== null)
			$hiero->SetAttribute(WH_Attribute::Code, $code);
		if($phoneme !== null)
			$hiero->SetAttribute(WH_Attribute::Phoneme, $phoneme);

		return $hiero;
	}

	/**
	 * Create a new row in the current page.
	 * @param   $parent  The parent where to add the line [WH_Node].
	 * @param   $op		The group operator value.
	 * @return  Reference to the created node
	 */
	public function &AddLayout(&$parent, $op = null)
	{
		switch($parent->GetClass())
		{
         case WH_CLASS_LINE:
         case WH_CLASS_LAYOUT:
         case WH_CLASS_GROUP:
         case WH_CLASS_FRAME:
				break;
			default:
				WH_Error("Try to create a hieroglyphs-group in a non-valid node!");
		}

		$this->group = &$parent->AddChild(WH_CLASS_LAYOUT);
		if($op !== null)
			$this->group->SetAttribute(WH_Attribute::Operator, $op);

		return $this->group;
	}

	/**
	 * Create a new cartouche
	 * @param   $parent  The parent where to add the line [WH_Node].
	 * @param   $type		The cartouche type [string=null].
	 * @return  Reference to the created node
	 */
	public function &AddFrame(&$parent, $type = null)
	{
		if($parent->GetClass() != WH_CLASS_LINE)
			WH_Error("Try to create a hieroglyphs-group in a non-valid node!");

		$this->cartouche = &$parent->AddChild(WH_CLASS_FRAME);
		if($type !== null)
			$this->cartouche->SetAttribute(WH_Attribute::Type, $type);

		return $this->cartouche;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a new section in the document.
	 * @param   $parent  The parent where to add the area [WH_Node=$this->root].
	 * @param   $type    The type of the area [string=null].
	 * @param   $name    The name of the area [string=null].
	 * @return  Reference to the created node
	 */
	public function &AddSection(&$parent = null, $type = null, $name = null, $ref = null)
	{
		if($parent == null)
			$parent = &$this->syntax;
      if($parent == null)
			WH_Error("Try to create an area in a non-initialized document!");

		$level = 0;
		if($parent->IsAttributeSet(WH_Attribute::Level))
			$level = $parent->GetAttribute(WH_Attribute::Level);
		$level++;

		$this->section = &$parent->AddChild(WH_CLASS_SECTION);
		$this->section->SetAttribute(WH_Attribute::Level, $level);
		if($type !== null)
			$this->section->SetAttribute(WH_Attribute::Type, $type);
		if($name !== null)
			$this->section->SetAttribute(WH_Attribute::Name, $name);
		if($ref !== null)
			$this->section->SetAttribute(WH_Attribute::Reference, $ref);

		return $this->section;
	}

	/// Get current section.
	public function &GetCurrentSection() { return $this->section; }
	
	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a new section in the document.
	 * @param   $parent  The parent where to add the area [WH_Node=$this->root].
	 * @return  Reference to the created node
	 */
	public function &AddSentence(&$parent = null, $ref = null)
	{
		if($parent == null)
			$parent = &$this->section;
		if($parent == null)
			WH_Error("Try to create an sentence in a non section node!");

		$this->sentence = &$parent->AddChild(WH_CLASS_SENTENCE);
		if($ref !== null)
			$this->sentence->SetAttribute(WH_Attribute::Reference, $ref);

		return $this->sentence;
	}

	/// Get current sentence.
	public function &GetCurrentSentence() { return $this->sentence;  }
	
	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a new section in the document.
	 * @param   $parent  The parent where to add the area [WH_Node=$this->root].
	 * @return  Reference to the created node
	 */
	public function &AddWord(&$parent = null, $ref = null)
	{
		if($parent == null)
			$parent = &$this->sentence;
		if($parent == null)
			WH_Error("Try to create a word in a non sentence node!");

		$this->word = &$parent->AddChild(WH_CLASS_WORD);
		if($ref !== null)
			$this->word->SetAttribute(WH_Attribute::Reference, $ref);

		return $this->word;
	}

	/// Get current word.
	public function &GetCurrentWord() { return $this->word; }
	
	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a new section in the document.
	 * @param   $parent  The parent where to add the area [WH_Node=$this->root].
	 * @param   $type    The type of the area [string=null].
	 * @param   $name    The name of the area [string=null].
	 * @return  Reference to the created node
	 */
	public function &AddLexeme(&$parent = null, $type = null, $ref = null)
	{
		if($parent == null)
			$parent = &$this->word;
      if($parent == null)
			WH_Error("Try to create an sentence in a non section node!");

		$lexeme = &$parent->AddChild(WH_CLASS_LEXEME);
		if($type !== null)
      	$lexeme->SetAttribute(WH_Attribute::Type, $type);
		if($ref !== null)
      	$lexeme->SetAttribute(WH_Attribute::Reference, $ref);

		return $lexeme;
	}

	////////////////////////////////////////////////////////////////////////////////
	/**
	 *
	 */
	public function DebugPrint()
	{
		$this->depth = 0;
		$this->PrintNode($this->root);
	}

	/// Get class name
	public static function GetClassName($class)
	{
		switch($class)
		{
			case WH_CLASS_DOC:        return "doc";
			case WH_CLASS_HEADER:     return "header";
			case WH_CLASS_AUTHOR:     return "author";
			case WH_CLASS_DATE:       return "date";
			case WH_CLASS_BIBLIO:     return "biblio";
			case WH_CLASS_REFERENCE:  return "reference";
			case WH_CLASS_AREA:       return "area";
			case WH_CLASS_LINE:       return "line";
			case WH_CLASS_HIEROGLYPH: return "hiero";
			case WH_CLASS_FRAME:      return "frame";
			case WH_CLASS_LAYOUT:     return "layout";
			case WH_CLASS_GROUP:      return "group";
			case WH_CLASS_TYPO:       return "typo";
			case WH_CLASS_SYNTAX:     return "syntax";
			case WH_CLASS_SECTION:    return "section";
			case WH_CLASS_SENTENCE:   return "sentence";
			case WH_CLASS_WORD:       return "word";
			case WH_CLASS_LEXEME:     return "lexeme";
			case WH_CLASS_DESC:       return "desc";
			case WH_CLASS_LOCALIZE:   return "loc";
			default:                  return "unknow";
		}
	}

	/// Get class id
	public static function GetClassId($class)
	{
		switch($class)
		{
			case "doc":        return WH_CLASS_DOC;
			case "header":     return WH_CLASS_HEADER;
			case "author":     return WH_CLASS_AUTHOR;
			case "date":       return WH_CLASS_DATE;
			case "biblio":     return WH_CLASS_BIBLIO;
			case "reference":  return WH_CLASS_REFERENCE;
			case "area":       return WH_CLASS_AREA;
			case "line":       return WH_CLASS_LINE;
			case "hiero":      return WH_CLASS_HIEROGLYPH;
			case "frame":      return WH_CLASS_FRAME;
			case "layout":     return WH_CLASS_LAYOUT;
			case "group":      return WH_CLASS_GROUP;
			case "typo":       return WH_CLASS_TYPO;
			case "syntax":     return WH_CLASS_SYNTAX;
			case "section":    return WH_CLASS_SECTION;
			case "sentence":   return WH_CLASS_SENTENCE;
			case "word":       return WH_CLASS_WORD;
			case "lexeme":     return WH_CLASS_LEXEME;
			case "desc":       return WH_CLASS_DESC;
			case "loc":        return WH_CLASS_LOCALIZE;
			default:           return -1;
		}
	}

	/// Tell if given class can be referenced
	public static function IsClassReferenced($class)
	{
		switch($class)
		{
			case WH_CLASS_HIEROGLYPH:
				return true;
			default:
				return false;
		}
	}

	/**
	 *
	 */
	protected function PrintNode(&$node)
	{
		for($i=0; $i<$this->depth; $i++)
			echo "&nbsp;&nbsp;&nbsp;";
			//echo chr(0x09);

		echo "<b>[".WH_Document::GetClassName($node->GetClass())."]</b>";

		$content = null;
		foreach($node->GetAttributes() as $key => $value)
		{
			if($key == WH_Attribute::Content)
				$content = $value;
			else
				echo " $key=\"".trim($value)."\"";
		}
		if($content)
			echo " [[$content]]";

		echo "<br/>\n";

		$this->depth++;
		foreach($node->GetChildren() as $child)
			$this->PrintNode($child);
		$this->depth--;
	}
}

?>